/**
 * Project Name:mauto-web
 * File Name:ApiJsDicController.java
 * Package Name:com.inftech.mauto.jy.web.jsPad
 * Date:2017年6月16日下午3:01:48
 * Copyright (c) 2017, inf-technology Co., Ltd. All Rights Reserved.
 *
*/

package com.inftech.mauto.jy.web.jsPad;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.alipay.api.AlipayResponse;
import com.alipay.api.response.AlipayTradePrecreateResponse;
import com.alipay.demo.trade.config.Configs;
import com.alipay.demo.trade.model.ExtendParams;
import com.alipay.demo.trade.model.GoodsDetail;
import com.alipay.demo.trade.model.builder.AlipayTradePayRequestBuilder;
import com.alipay.demo.trade.model.builder.AlipayTradePrecreateRequestBuilder;
import com.alipay.demo.trade.model.result.AlipayF2FPayResult;
import com.alipay.demo.trade.model.result.AlipayF2FPrecreateResult;
import com.alipay.demo.trade.service.AlipayTradeService;
import com.alipay.demo.trade.service.impl.AlipayTradeServiceImpl;
import com.inftech.mauto.common.base.BaseController;
import com.inftech.mauto.common.base.BusinessException;
import com.inftech.mauto.common.constant.HttpStatusCodeEnum;
import com.inftech.mauto.common.pay.AlipayNotify;
import com.inftech.mauto.jy.entity.JyOrder;
import com.inftech.mauto.jy.service.JyOrderService;

/**
 * ClassName:ApiJsDicController <br/>
 * Function: 支付宝支付接口 <br/>
 * Date: 2017年6月23日 下午5:01:48 <br/>
 * 
 * @author hx
 * @version
 * @since JDK 1.7
 */
@Controller
@RequestMapping(value = "/api/js/aliPay", produces = "application/json;charset=utf-8")
public class ApiAliPayController extends BaseController {

	Log log = LogFactory.getLog(ApiAliPayController.class);

	@Resource
	private JyOrderService jyOrderServiceImpl;

	// 支付宝当面付2.0服务
	private static AlipayTradeService tradeService;

	static {
		/**
		 * 一定要在创建AlipayTradeService之前调用Configs.init()设置默认参数
		 * Configs会读取classpath下的zfbinfo.properties文件配置信息，
		 * 如果找不到该文件则确认该文件是否在classpath目录
		 */
		Configs.init("zfbinfo.properties");

		/**
		 * 使用Configs提供的默认参数 AlipayTradeService可以使用单例或者为静态成员对象，不需要反复new
		 */
		tradeService = new AlipayTradeServiceImpl.ClientBuilder().build();

	}

	/**
	 * 
	 * tradePay:扫码支付,同步返回结果. <br/>
	 *
	 * @author Administrator
	 * @param request
	 * @param response
	 * @since JDK 1.7
	 */
	@RequestMapping(value = "/tradePay")
	@ResponseBody
	public String tradePay(HttpServletRequest request, HttpServletResponse response) {
		String outJson = "";
		try {
			String orderCode = request.getParameter("orderCode");
			Map<String, Object> map = new HashMap<String, Object>();
			map.put("orderCode", orderCode);
			JyOrder jyOrder = jyOrderServiceImpl.getOrderDetail(map);
			if (jyOrder != null) {
				// (必填) 商户网站订单系统中唯一订单号，64个字符以内，只能包含字母、数字、下划线，
				// 需保证商户系统端不能重复，建议通过数据库sequence生成，
				String outTradeNo = orderCode + "_" + System.currentTimeMillis();

				// (必填) 订单标题，粗略描述用户的支付目的。如“xxx品牌xxx门店消费”
				String subject = "米拓车服上门保养";

				// (必填) 订单总金额，单位为元，不能超过1亿元
				// 如果同时传入了【打折金额】,【不可打折金额】,【订单总金额】三者,则必须满足如下条件:【订单总金额】=【打折金额】+【不可打折金额】
				String totalAmount = jyOrder.getPaySum();

				// (必填) 付款条码，用户支付宝钱包手机app点击“付款”产生的付款条码
				String authCode = request.getParameter("authCode");

				// 卖家支付宝账号ID，用于支持一个签约账号下支持打款到不同的收款账号，(打款到sellerId对应的支付宝账号)
				// 如果该字段为空，则默认为与支付宝签约的商户的PID，也就是appid对应的PID
				String sellerId = "";

				// 订单描述，可以对交易或商品进行一个详细地描述，比如填写"购买商品3件共20.00元"
				List<Map<String, Object>> wxxmDetailList = jyOrderServiceImpl.getWxxmDetail(map);

				String body = "购买项目" + wxxmDetailList.size() + "件,共" + totalAmount + "元";

				// 商户操作员编号，添加此参数可以为商户操作员做销售统计
				String operatorId = "test_operator_id";

				// (必填) 商户门店编号，通过门店号和商家后台可以配置精准到门店的折扣信息，详询支付宝技术支持
				String storeId = "test_store_id";

				// 业务扩展参数，目前可添加由支付宝分配的系统商编号(通过setSysServiceProviderId方法)，详情请咨询支付宝技术支持
				String providerId = "2088100200300400500";
				ExtendParams extendParams = new ExtendParams();
				extendParams.setSysServiceProviderId(providerId);

				// 支付超时，线下扫码交易定义为5分钟
				String timeoutExpress = "5m";

				// 商品明细列表，需填写购买商品详细信息，
				// List<GoodsDetail> goodsDetailList = new
				// ArrayList<GoodsDetail>();
				// for (Map<String, Object> wx : wxxmDetailList) {
				// //
				// 创建一个商品信息，参数含义分别为商品id（使用国标）、名称、单价（单位为分）、数量，如果需要添加商品类别，详见GoodsDetail
				// GoodsDetail goods1 = GoodsDetail.newInstance("goods_id001",
				// "xxx面包", 1000, 1);
				// // 创建好一个商品后添加至商品明细列表
				// goodsDetailList.add(goods1);
				// }

				// String appAuthToken = "应用授权令牌";//根据真实值填写

				// 创建条码支付请求builder，设置请求参数
				AlipayTradePayRequestBuilder builder = new AlipayTradePayRequestBuilder()
						// .setAppAuthToken(appAuthToken)
						.setOutTradeNo(outTradeNo).setSubject(subject).setAuthCode(authCode).setTotalAmount(totalAmount)
						.setStoreId(storeId).setBody(body).setOperatorId(operatorId).setExtendParams(extendParams)
						.setSellerId(sellerId)
						// .setGoodsDetailList(goodsDetailList)
						.setTimeoutExpress(timeoutExpress);

				// 调用tradePay方法获取当面付应答
				AlipayF2FPayResult result = tradeService.tradePay(builder);
				HttpStatusCodeEnum errorstatusCode = HttpStatusCodeEnum.STATUS_CODE_708;
				switch (result.getTradeStatus()) {
				case SUCCESS:
					log.info("支付宝支付成功: ");
					jyOrderServiceImpl.updatePayStatusByOrderCode(map);
					HttpStatusCodeEnum statusCode = HttpStatusCodeEnum.STATUS_CODE_200;
					outJson = getOutJson(statusCode, true, statusCode.toString(), null);
					break;

				case FAILED:
					log.error("支付宝支付失败!!!");
					log.error("不支持的交易状态，交易返回异常!!!");
					outJson = getOutJson(errorstatusCode, false, "不支持的交易状态，交易返回异常!!!", null);
					break;

				case UNKNOWN:
					log.error("系统异常，订单状态未知!!!");
					log.error("不支持的交易状态，交易返回异常!!!");
					outJson = getOutJson(errorstatusCode, false, "不支持的交易状态，交易返回异常!!!", null);
					break;

				default:
					log.error("不支持的交易状态，交易返回异常!!!");
					log.error("不支持的交易状态，交易返回异常!!!");
					outJson = getOutJson(errorstatusCode, false, "不支持的交易状态，交易返回异常!!!", null);
					break;
				}
			} else {
				log.error("查不到订单");
				throw new NullPointerException("查不到订单");
			}
		} catch (Exception e) {
			if (e instanceof BusinessException) {
				throw e;
			}
			LOG.error("支付失败", e);
			HttpStatusCodeEnum statusCode = HttpStatusCodeEnum.SCOPE_BASE;
			outJson = getOutJson(statusCode, false, statusCode.toString(), null);
		}
		return outJson;
	}

	/**
	 * 
	 * getNotify:异步通知. <br/>
	 *
	 * @author Administrator
	 * @param request
	 * @param response
	 * @return
	 * @throws UnsupportedEncodingException
	 * @since JDK 1.7
	 */
	@RequestMapping(value = "/getNotify")
	@ResponseBody
	public String getNotify(HttpServletRequest request, HttpServletResponse response)
			throws UnsupportedEncodingException {
		Map<String, String> params = new HashMap<String, String>();
		Map requestParams = request.getParameterMap();

		for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) {
			String name = (String) iter.next();
			String[] values = (String[]) requestParams.get(name);
			String valueStr = "";
			for (int i = 0; i < values.length; i++) {
				valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
			}
			// 乱码解决，这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化
			// valueStr = new String(valueStr.getBytes("ISO-8859-1"), "gbk");
			params.put(name, valueStr);
		}

		// 获取支付宝的通知返回参数，可参考技术文档中页面跳转同步通知参数列表(以下仅供参考)//
		// 商户订单号
		String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"), "UTF-8");

		// 支付宝交易号
		String trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"), "UTF-8");

		// 交易状态
		String trade_status = new String(request.getParameter("trade_status").getBytes("ISO-8859-1"), "UTF-8");

		// 异步通知ID
		String notify_id = request.getParameter("notify_id");

		// sign
		String sign = request.getParameter("sign");

		if (notify_id != "" && notify_id != null) {
			if (AlipayNotify.verifyResponse(notify_id).equals("true")) // 判断成功之后使用getResponse方法判断是否是支付宝发来的异步通知。
			{
				if (AlipayNotify.getSignVeryfy(params, sign)) // 使用支付宝公钥验签
				{
					if (trade_status.equals("TRADE_FINISHED") || trade_status.equals("TRADE_SUCCESS")) {
						// 业务处理
						Map<String, Object> map = new HashMap<String, Object>();
						String orderCode = out_trade_no.substring(0, out_trade_no.lastIndexOf("_"));
						map.put("orderCode", orderCode);
						jyOrderServiceImpl.updatePayStatusByOrderCode(map);
						return "success";
					} else {
						return "trade_status fail";
					}
				} else// 验证签名失败
				{
					return "sign fail";
				}
			} else// 验证是否来自支付宝的通知失败
			{
				return "response fail";
			}
		} else {
			return "no notify message";
		}
	}

	/**
	 * 
	 * tradePreCreate:刷卡支付，返回支付链接. <br/>
	 *
	 * @author Administrator
	 * @param request
	 * @param response
	 * @return
	 * @since JDK 1.7
	 */
	@RequestMapping(value = "/tradePreCreate")
	@ResponseBody
	public String tradePreCreate(HttpServletRequest request, HttpServletResponse response) {
		String outJson = "";
		try {
			String orderCode = request.getParameter("orderCode");
			Map<String, Object> map = new HashMap<String, Object>();
			map.put("orderCode", orderCode);
			JyOrder jyOrder = jyOrderServiceImpl.getOrderDetail(map);
			if (jyOrder != null) {

				// (必填) 商户网站订单系统中唯一订单号，64个字符以内，只能包含字母、数字、下划线，
				// 需保证商户系统端不能重复，建议通过数据库sequence生成，
				String outTradeNo = orderCode + "_" + System.currentTimeMillis();

				// (必填) 订单标题，粗略描述用户的支付目的。如“xxx品牌xxx门店消费”
				String subject = "米拓车服上门保养";

				// (必填) 订单总金额，单位为元，不能超过1亿元
				// 如果同时传入了【打折金额】,【不可打折金额】,【订单总金额】三者,则必须满足如下条件:【订单总金额】=【打折金额】+【不可打折金额】
				String totalAmount = jyOrder.getPaySum();

				// 卖家支付宝账号ID，用于支持一个签约账号下支持打款到不同的收款账号，(打款到sellerId对应的支付宝账号)
				// 如果该字段为空，则默认为与支付宝签约的商户的PID，也就是appid对应的PID
				String sellerId = "";

				// 订单描述，可以对交易或商品进行一个详细地描述，比如填写"购买商品3件共20.00元"
				List<Map<String, Object>> wxxmDetailList = jyOrderServiceImpl.getWxxmDetail(map);

				String body = "购买项目" + wxxmDetailList.size() + "件,共" + totalAmount + "元";

				// 商户操作员编号，添加此参数可以为商户操作员做销售统计
				String operatorId = "test_operator_id";

				// (必填) 商户门店编号，通过门店号和商家后台可以配置精准到门店的折扣信息，详询支付宝技术支持
				String storeId = "test_store_id";

				// 业务扩展参数，目前可添加由支付宝分配的系统商编号(通过setSysServiceProviderId方法)，详情请咨询支付宝技术支持
				ExtendParams extendParams = new ExtendParams();
				extendParams.setSysServiceProviderId("2088100200300400500");

				// 支付超时，定义为120分钟
				String timeoutExpress = "120m";

				// 商品明细列表，需填写购买商品详细信息，
				List<GoodsDetail> goodsDetailList = new ArrayList<GoodsDetail>();
				// 创建一个商品信息，参数含义分别为商品id（使用国标）、名称、单价（单位为分）、数量，如果需要添加商品类别，详见GoodsDetail
				GoodsDetail goods1 = GoodsDetail.newInstance("goods_id001", "xxx小面包", 1000, 1);
				// 创建好一个商品后添加至商品明细列表
				goodsDetailList.add(goods1);

				// 继续创建并添加第一条商品信息，用户购买的产品为“黑人牙刷”，单价为5.00元，购买了两件
				GoodsDetail goods2 = GoodsDetail.newInstance("goods_id002", "xxx牙刷", 500, 2);
				goodsDetailList.add(goods2);

				// 创建扫码支付请求builder，设置请求参数
				AlipayTradePrecreateRequestBuilder builder = new AlipayTradePrecreateRequestBuilder()
						.setSubject(subject).setTotalAmount(totalAmount).setOutTradeNo(outTradeNo).setSellerId(sellerId)
						.setBody(body).setOperatorId(operatorId).setStoreId(storeId).setExtendParams(extendParams)
						.setTimeoutExpress(timeoutExpress)
						// .setNotifyUrl("http://www.test-notify-url.com")//支付宝服务器主动通知商户服务器里指定的页面http路径,根据需要设置
						.setGoodsDetailList(goodsDetailList);

				AlipayF2FPrecreateResult result = tradeService.tradePrecreate(builder);
				HttpStatusCodeEnum errorstatusCode = HttpStatusCodeEnum.SCOPE_BASE;
				switch (result.getTradeStatus()) {
				case SUCCESS:
					log.info("支付宝预下单成功: )");

					AlipayTradePrecreateResponse response1 = result.getResponse();
					dumpResponse(response1);

					// 需要修改为运行机器上的路径
					// String filePath =
					// String.format("/Users/sudo/Desktop/qr-%s.png",
					// response1.getOutTradeNo());
					// log.info("filePath:" + filePath);
					// ZxingUtils.getQRCodeImge(response.getQrCode(), 256,
					// filePath);
					HttpStatusCodeEnum statusCode = HttpStatusCodeEnum.STATUS_CODE_708;
					Map<String, Object> resultMap = new HashMap<String, Object>();
					resultMap.put("urlCode", response1.getQrCode());
					outJson = getOutJson(statusCode, true, statusCode.toString(), resultMap);
					break;

				case FAILED:
					log.error("支付宝预下单失败!!!");
					outJson = getOutJson(errorstatusCode, false, "支付宝预下单失败!!!", null);
					break;

				case UNKNOWN:
					log.error("系统异常，预下单状态未知!!!");
					outJson = getOutJson(errorstatusCode, false, "系统异常，预下单状态未知!!!", null);
					break;

				default:
					log.error("不支持的交易状态，交易返回异常!!!");
					outJson = getOutJson(errorstatusCode, false, "不支持的交易状态，交易返回异常!!!", null);
					break;
				}
			} else {
				log.error("查不到订单");
				throw new NullPointerException("查不到订单");
			}

		} catch (Exception e) {
			if (e instanceof BusinessException) {
				throw e;
			}
			LOG.error("支付失败", e);
			HttpStatusCodeEnum statusCode = HttpStatusCodeEnum.SCOPE_BASE;
			outJson = getOutJson(statusCode, false, statusCode.toString(), null);
		}
		return outJson;
	}

	// 简单打印应答
	private void dumpResponse(AlipayResponse response) {
		if (response != null) {
			log.info(String.format("code:%s, msg:%s", response.getCode(), response.getMsg()));
			if (StringUtils.isNotEmpty(response.getSubCode())) {
				log.info(String.format("subCode:%s, subMsg:%s", response.getSubCode(), response.getSubMsg()));
			}
			log.info("body:" + response.getBody());
		}
	}

}
